﻿using Collections.Pooled;
using CSRedis;
using DotNetCommon;
using furion.blog.Domain.MySQL;
using furion.blog.Domain.Shared;
using furion.blog.Domain.Shared.Attribute;
using furion.blog.Domain.Shared.Enum;
using furion.blog.Utils;
using furion.blog.Utils.Extensions;
using Furion.UnifyResult;
using IPTools.Core;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Controllers;
using Microsoft.AspNetCore.Mvc.Filters;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Wangkanai.Detection.Services;

namespace furion.blog.Web.Core.Handlers
{
    /// <summary>
    /// 请求过滤器
    /// </summary>
    internal class RequestFilter : IActionFilter
    {
        Stopwatch watch = null;
        private readonly CSRedisClient[] _csredis;
        private readonly IHttpContextAccessor _accessor;
        private readonly IDetectionService _detection;
        private string body;

        public RequestFilter(CSRedisClient[] csredis, IHttpContextAccessor accessor, IDetectionService detection)
        {
            this._csredis = csredis;
            this._accessor = accessor;
            this._detection = detection;
        }
        /// <summary>
        /// 执行中
        /// </summary>
        /// <param name="context"></param>
        public void OnActionExecuting(ActionExecutingContext context)
        {
            watch = new Stopwatch();
            watch.Start();
            //操作执行前做的事情
            Console.WriteLine("请求URI===>" + context.HttpContext.Request.Path);
            body = context.HttpContext.Request.GetBodyData();

            //PathString requestPath = context.HttpContext.Request.Path;
            //string reqMethod = context.HttpContext.Request.Method;

        }
        /// <summary>
        /// 执行后
        /// </summary>
        /// <param name="context"></param>
        public void OnActionExecuted(ActionExecutedContext context)
        {
            string responseBody = null;
            //操作执行后做的事情
            watch.Stop();
            long usedMs = watch.ElapsedMilliseconds;
            watch = null;
            Console.WriteLine(context.Result);
            if (context.Result is JsonResult && ((JsonResult)context.Result).Value is RESTfulResult<object>)
            {
                Console.WriteLine("输出结果是RESTfulResult");
                JsonResult jr = ((JsonResult)context.Result);
                Dictionary<string, object> map = new Dictionary<string, object>();
                map.Add("usedTime", usedMs);
                ((RESTfulResult<object>)jr.Value).Extras = map;
                responseBody = JsonConvert.SerializeObject(jr.Value);
            }
            else if (context.Result is ObjectResult)
            {
                Console.WriteLine("输出结果是ObjectResult");
                if (((ObjectResult)context.Result).Value is ResultVO)
                {
                    ((ResultVO)((ObjectResult)context.Result).Value).usedTime = usedMs;
                }
                responseBody = JsonConvert.SerializeObject(((ObjectResult)context.Result).Value);
            }
            else if (context.Result is ResultVO)
            {
                Console.WriteLine("输出结果是ResultVO");
                ((ResultVO)context.Result).usedTime = usedMs;
            }
            Console.WriteLine("相应内容：" + responseBody);
            Console.WriteLine("请求耗时：" + usedMs + "ms");
            var (role, uid) = VisitorInfo(context);

            
            var hasUploadFilesCount = context.HttpContext.Request.HasFormContentType && context.HttpContext.Request.Form?.Files.Count>0;

            if (!hasUploadFilesCount)
            {
                //获取调用的方法信息
                var (classMethod, apiName, apiDesc) = GetClassMethod(context);
                //写入redis缓存，利用定时执行写入数据库
                WriteRedisRequestLog(
                    context.HttpContext.Request.Method,
                    context.HttpContext.Request.Path.Value,
                    body,
                    string.IsNullOrEmpty(responseBody) ? "" : responseBody,
                    usedMs,
                    role,
                    uid,
                    classMethod,
                    apiName,
                    apiDesc
                    );
            }
        }

        /// <summary>
        /// 获取当前请求调用了那个接口
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        private (string,string,string) GetClassMethod(ActionExecutedContext context) {
            //string controller = context.Controller.GetType().FullName;
            //转换ActionDescriptor 
            var controllerActionDescriptor = context.ActionDescriptor as ControllerActionDescriptor;

            ActionAttribute actionAttribute = controllerActionDescriptor.MethodInfo
            .GetCustomAttributes(typeof(ActionAttribute), false).FirstOrDefault() as ActionAttribute;

            string action = context.ActionDescriptor.DisplayName;
            string apiName = string.Empty;
            string apiDesc = string.Empty;

            if (actionAttribute != null) {
                apiName = actionAttribute.Name;
                apiDesc = actionAttribute.Desc;
            }
            return (action,apiName,apiDesc);
        }

        /// <summary>
        /// 角色
        /// </summary>
        private PooledSet<string> Roles = new PooledSet<string> {"User","Member"};

        /// <summary>
        /// 获取当前访问的人员信息
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        private (string, long) VisitorInfo(ActionExecutedContext context) {
            if (context.HttpContext.Request.Headers.ContainsKey("UserRole")) {
                string role = context.HttpContext.Request.Headers["UserRole"].First<string>();
                if (Roles.Contains(role)) {
                    return (role,VisitorId(context));
                }
            }
            return ("Unknow",0L);
        }

        /// <summary>
        /// 当前登录账号ID
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        private long VisitorId(ActionExecutedContext context) {
            return string.IsNullOrEmpty(context.HttpContext.Request.Headers["UserId"].ToString())?0L:long.Parse(context.HttpContext.Request.Headers["UserId"].ToString());
        }


        /// <summary>
        /// 本地ip，无法进行解析
        /// </summary>
        private static HashSet<string> WhiteIps = new HashSet<string>() { 
            NetworkHelper.GetLocalIPAddress().ToString(),
            "127.0.0.1",
            "0.0.0.1",
            "0.0.0.0" 
        };
        
        /// <summary>
        /// 写入redis缓存
        /// </summary>
        /// <param name="requestMethod"></param>
        /// <param name="url"></param>
        /// <param name="body"></param>
        /// <param name="result"></param>
        /// <param name="usedTime"></param>
        /// <param name="role"></param>
        /// <param name="uid"></param>
        /// <param name="classMethod"></param>
        /// <param name="apiName"></param>
        /// <param name="apiDesc"></param>
        private void WriteRedisRequestLog(
            string requestMethod, 
            string url, 
            string body, 
            string result, 
            long usedTime,
            string role,
            long uid,
            string classMethod,
            string apiName,
            string apiDesc
            )
        {
            string ip = _accessor.HttpContext?.Connection?.RemoteIpAddress?.ToString();
            string sourceAddr = "";


            /*if (!WhiteIps.Contains(ip))
            {
                IpInfo ipInfo = IpTool.Search(ip);
                sourceAddr = ipInfo.Country + "/" + ipInfo.Province + "/" + ipInfo.City;
            }*/
            try
            {
                IpInfo ipInfo = IpTool.Search(ip);
                sourceAddr = ipInfo.Country + "/" + ipInfo.Province + "/" + ipInfo.City;
            }
            catch {
                sourceAddr = "";
            }
           
            var record = new RequestLog
            {
                LogId = IdUtil.Id(),
                RequestMethod = requestMethod,
                Ip = ip,
                SourceAddr = sourceAddr,
                //客户端信息
                OSInfo = JsonConvert.SerializeObject(new
                {
                    deviceType = this._detection.Device.Type.ToString(),
                    browserName = this._detection.Browser.Name.ToString(),
                    platFormName = this._detection.Platform.Name.ToString(),
                    userAgent = this._detection.UserAgent.ToString()
                }),
                Url = url,
                Body = body,
                Result = String.Empty,
                UsedTime = usedTime,
                CreatedTime = DateTime.Now,
                Role = role,
                Uid = uid,
                ExecuteMethod = classMethod,
                ApiName = apiName,
                ApiDesc = apiDesc,
            };
            _csredis[14].LPush("RequestLog", JsonConvert.SerializeObject(record));
        }

      
    }
}

